#!/usr/bin/perl

use strict;

use IMDB::Film;
use File::Basename;
use Getopt::Long;
use XML::Simple;

#for debugging
use Data::Dumper;

my @local_episodes = ();
my $wanted_id = undef;
my $show = undef;
my %options = undef;
my $option_help;

sub display_usage;
sub usage_error;

Getopt::Long::Configure ('bundling');
GetOptions (
	'v|verbose'		=> \$options{verbose},
	't|title=s'		=> \$options{title},
	'd|directory=s'	=> \$options{directory},
	'y|year=s' 		=> \$options{year},
	'p|prompt' 		=> \$options{prompt},
	's|skip' 		=> \$options{type},
	'x|xml'			=> \$options{xml},
	'h|help'		=> \$option_help,
) or usage_error;

display_usage if $option_help;

display_usage
	unless ($options{directory} && $options{title});

readFiles();

unless ($options{year}) {
	$show = new IMDB::Film(crit => $options{title})
		or die "IMDb failed!";
} else {
	$show = new IMDB::Film(crit => $options{title}, year => $options{year})
                or die "IMDb failed!";
}
print "\n	Getting info for: ".$show->title()."\n\n";

unless ($show->status() && $show->kind() eq 'tv series') {
	print "\nIt seems that \"".$show->title()."\" is no TV show.\n";
	print "Type: ".$show->kind()."\n";
    print "Tagging movies is not supported. Please use the internal WDTV tagging tool for movies.";
	exit 1;
}

# Possible values are: 'movie', 'tv series', 'tv mini series', 'video game', 'video movie', 'tv movie', 'episode'.

my @imdb_episodes = @{ $show->episodes() };

print "Processing show ".$show->title()."\n"
    if ($options{verbose});

foreach my $local (@local_episodes) {
	foreach my $remote (@imdb_episodes) {
		if ($local->{'season'} == $remote->{'season'} 
		&& $local->{'episode'} == $remote->{'episode'}) {
			$local->{'id'} = $remote->{'id'};
			$local->{'year'} = validateDateFormat($remote->{'date'});
			$local->{'title'} = $remote->{'title'};
			$local->{'plot'} = $remote->{'plot'};
			print "ID, date, title and plot added for episode S".$local->{'season'}."E".$local->{'episode'}."\n"
				if ($options{verbose});
		}
	}
}


foreach my $local (@local_episodes) {
	getEpisodeInfo($local);
}


if ($options{verbose}) {
	print "\n\n Episode summary:\n\n";
	foreach my $hash_ref (@local_episodes) {
		foreach my $key (keys %$hash_ref) {
			print $key.": ".$hash_ref->{$key}."\n";
		}
		print "\n";
	}
}

if($options{xml}) {
	writeXML();
}

sub readFiles {
	my @files = <$options{directory}/*>;
	my $i = 0;
	
	print "\n\n			Tag 'em!\n\n";
	unless (-d $options{directory}) {
		print "Directory does not exist: ".$options{directory}."\n";
		exit 1;
	}
	print "Analyzing files in ".$options{directory}."\n";
	
	foreach my $file (@files) {
		validateFile($file);
	}
	
	if (scalar @local_episodes == 1) {
		print "\n".scalar @local_episodes." valid file found\n";
	} elsif (scalar @local_episodes == 0) {
		print "\n\nNo valid files found. Quitting...\n\n";
		exit 1;
	} else {
		print "\n".scalar @local_episodes." valid files found\n";
	}
}

sub validateFile {
	my $file = shift;
	
	(my $filename,my $dir,my $ext) = fileparse($file, qr/\.[^.]*/);
	print "\nFile ".$filename.$ext." found in ".$dir."\nProcessing...\n"
		if $options{verbose};
	
	#file type check
	unless ($options{type}) {
		unless (validFiletype($ext)) {
			print "Invalid video file found. Skipping...\n"
				if $options{verbose};
			return 0;
		}
	}
	
	my $season = getSeason($file, 0);
	my $episode = getEpisode($file, 0);
	
	if (defined $season && defined $episode) {
		print "Valid file ".$filename.$ext." found in ".$dir."\n";
		print "Episode: ".$episode."  Season: ".$season."\n";
		my %episode = ();
		$episode{filename} = $filename;
		$episode{extension} = $ext;
		$episode{directory} = $dir;
		$episode{season} = $season;
		$episode{episode} = $episode;
		push(@local_episodes, \%episode);
		return 1;
		#$i++;
	} else {
		print "No valid season and/or episode information found in ".$file."\n";
		print "Season value: ".$season."\nEpisode value: ".$episode."\n"
			if $options{verbose};
		return 0;
	}
	
}

sub is_integer {
	defined $_[0] && $_[0] =~ /^[+-]?\d+$/;
}

sub getSeason {
	my ($file, $check) = @_;
	my $season = undef;

	unless ($check) {
		
		#1st check checks for syntax like S02E12.
		#2nd check will be called if unsuccessful
		
		print "\nPerforming first season check...\n"
			if ($options{verbose});
		
		my $re1='.*?';	# Non-greedy match on filler
		my $re2='(S)';	# Any Single Character 1
		my $re3='(\\d)';	# Any Single Digit 1
		my $re4='(\\d)';	# Any Single Digit 2
		
		my $re=$re1.$re2.$re3.$re4;
		if ($file =~ m/$re/is) {
			my $c1=$1;
			my $d1=$2;
			my $d2=$3;
			
			if ($d1 == 0) {
				#			print "DEBUG: One digit season (".$d2.")\n";
				$season = $d2;
			} else {
				#			print "DEBUG: Two digit season (".$d1.$d2.")\n";
				$season = $d1.$d2;
			}
		}
		
		#check if 1st check was successful
		
		if (is_integer($season)) {
			print "Looks like the first season check was successful. Using value: ".$season."\n"
				if ($options{verbose});
			return $season;
		} else {
			print "Looks like the first season check wasn't successful (Value: ".$season.").\n"
				if ($options{verbose});
			#if prompt option was set, ask before performing 2nd check
			if ($options{prompt}) {
				print "\nDid I do anything wrong? Season value (".$season.") was recognized from file ".$file."\n";
				print "Is that correct? (y/n)";
				my $ok = <>;
				if ($ok =~ m/y/i) {
					return $season;
				} elsif ($ok =~ m/n/i) {
					print "Trying a different check...\n";
					getSeason($file, 1);
				} else {
					print "Input not recognized. Trying a different check...\n";
					getSeason($file, 1);
				}
				
			} else {
				print "Trying a different check...\n"
					if ($options{verbose});
				getSeason($file, 1);
			}
		}
	} else {
		# 2nd check 
		
		my $re1='.*?';	# Non-greedy match on filler
		my $re2='(\\d+)';	# Integer Number 1
		
		my $re=$re1.$re2;
		if ($file =~ m/$re/is) {
			$season=$1;
			print "2nd season check found value ".$season." in file ".$file."\n"
				if ($options{verbose});
			return $season;
		} else {
			
			#TODO: the 'skipping' machanism has to be implemented
			
			print "2nd season check was unsuccessful. Skipping file...\n\n";
			return undef;
		}
	}
}

sub getEpisode {
	my ($file, $check) = @_;
	my $episode = undef;
	
	unless ($check) {
		
		#first episode check for files like S03E12
		
		print "Performing first episode check...\n"
		if ($options{verbose});
		
		my $re1='.*?';	# Non-greedy match on filler
		my $re2='E';	# Uninteresting: c
		my $re3='.*?';	# Non-greedy match on filler
		my $re4='(E)';	# Any Single Character 1
		my $re5='(\\d)';	# Any Single Digit 1
		my $re6='(\\d)';	# Any Single Digit 2
		
		my $re=$re1.$re2.$re3.$re4.$re5.$re6;
		if ($file =~ m/$re/is) {
			my $c1=$1;
			my $d1=$2;
			my $d2=$3;
			
			if ($d1 == 0) {
				#print "DEBUG: One digit episode (".$d2.")\n";
				$episode = $d2;
			} else {
				#print "DEBUG: Two digit episode (".$d1.$d2.")\n";
				$episode = $d1.$d2;
			}
		}
		
		print "DEBUG: episode value (".$episode.") found on first check\n"
			if ($options{verbose});
		
		if (is_integer($episode)) {
			print "Looks like the first episode check was successful. Using value: ".$episode."\n"
				if ($options{verbose});
			return $episode;
		} else {
			print "Looks like the first episode check wasn't successful (Value: ".$episode.").\n"
				if ($options{verbose});
			#if prompt option was set, ask before performing 2nd check
			if ($options{prompt}) {
				print "\nDid I do anything wrong? Episode value (".$episode.") was recognized from file ".$file."\n";
				print "Is that correct? (y/n)";
				my $ok = <>;
				if ($ok =~ m/y/i) {
					return $episode;
				} elsif ($ok =~ m/n/i) {
					print "Trying a different check...\n";
					getEpisode($file, 1);
				} else {
					print "Input not recognized. Trying a different check...\n";
					getEpisode($file, 1);
				}
				
			} else {
				print "Trying a different check...\n"
					if ($options{verbose});
				getEpisode($file, 1);
			}
		}
	} else {
		
		#second episode check for files like 3x08
		
		my $re1='.*?';	# Non-greedy match on filler
		my $re2='\\d+';	# Uninteresting: int
		my $re3='.*?';	# Non-greedy match on filler
		my $re4='(\\d+)';	# Integer Number 1
		
		my $re=$re1.$re2.$re3.$re4;
		
		if ($file =~ m/$re/is) {
			$episode=$1;
			print "2nd episode check found value ".$episode." in file ".$file."\n"
				if ($options{verbose});
			return $episode;
		} else {
			
			#TODO: the 'skipping' machanism has to be implemented
			
			print "2nd episode check was unsuccessful. Skipping file...\n\n";
			return undef;
		}
		
	}
}

sub getEpisodeInfo {
	my ($local) = @_;
	unless (defined $local->{'id'}) {
		print "\nTrying to process invalid episode!\n";
		if ($options{verbose}) {
			print "Detailed episode info so far: \n";
			print "Title: ".$local->{'title'}."\n";
			print "ID: ".$local->{'id'}."\n";
			print "Season: ".$local->{'season'}."\n";
			print "Episode: ".$local->{'episode'}."\n";
			print "Date: ".$local->{'year'}."\n";
			print "Plot: ".$local->{'plot'}."\n\n";
		}
		print "Skipping episode...\n";
		return;
	}
	my $episode = new IMDB::Film(crit => $local->{'id'})
		or warn "Invalid IMDB ID (".$local->{'id'}.") found for episode ".$local->{'filename'}.$local->{'extension'}."\n";
	
	my @actors;
	foreach my $href (@{ $episode->cast() }) {
		my %actor;
		$actor{name} = $href->{name};
		$actor{role} = $href->{role};
		$actor{id} = $href->{id};
		push(@actors, \%actor);
		print "Actor ".$href->{name}." added for episode S".$local->{'season'}."E".$local->{'episode'}."\n"
			if ($options{verbose});
	}
	$local->{'actor'} = \@actors;
	
	if($episode->summary()) {
		$local->{'overview'} = $episode->summary();
		print "Summary ".$local->{'overview'}." added for episode S".$local->{'season'}."E".$local->{'episode'}."\n"
			if ($options{verbose});
	} else {
		$local->{'overview'} = $local->{'plot'};
		print "No summary delivered. Using plot as summary instead.\n"
			if ($options{verbose});
	}
	

	
	$local->{'mpaa'} = $episode->mpaa_info();
	print "MPAA ".$local->{'mpaa'}." added for episode S".$local->{'season'}."E".$local->{'episode'}."\n"
		if ($options{verbose});
		
	$local->{'runtime'} = $episode->duration();
	print "Runtime ".$local->{'runtime'}." added for episode S".$local->{'season'}."E".$local->{'episode'}."\n"
		if ($options{verbose});
		
	$local->{'rating'} = $episode->rating();
	print "Rating ".$local->{'rating'}." added for episode S".$local->{'season'}."E".$local->{'episode'}."\n"
		if ($options{verbose});
		
	my @genres = @{$episode->genres()};
	$local->{'genre'} = $episode->genres();
	if ($options{verbose}) {
		print scalar(@genres)." genres (";
		my $i;
		for ($i=0; $i<scalar(@genres)-1; $i++) {
			print $genres[$i].", ";
		}
		print $genres[$i].") added for episode S".$local->{'season'}."E".$local->{'episode'}."\n";
	}
	
	my @directors;
	foreach my $href (@{$episode->directors()}) {
		push(@directors, $href->{name});
		print "Director ".$href->{name}." added for episode S".$local->{'season'}."E".$local->{'episode'}."\n"
			if ($options{verbose});
	}
	$local->{'director'} = \@directors;
	
	$local->{'studio'} = $episode->company();
	print "Studio ".$local->{'studio'}." added for episode S".$local->{'season'}."E".$local->{'episode'}."\n"
		if ($options{verbose});
	$local->{'trivia'} = $episode->trivia();
	print "Trivia ".$local->{'trivia'}." added for episode S".$local->{'season'}."E".$local->{'episode'}."\n"
		if ($options{verbose});
	$local->{'aspect_ratio'} = $episode->aspect_ratio();
	print "Aspect Ratio ".$local->{'aspect_ratio'}." added for episode S".$local->{'season'}."E".$local->{'episode'}."\n"
		if ($options{verbose});
	my @awards = @{$episode->awards()};
	$local->{'awards'} = $episode->awards();
	if ($options{verbose}) {
		print scalar(@awards)." awards (";
		my $i;
		for ($i=0; $i<scalar(@awards)-1; $i++) {
			print $awards[$i].", ";
		}
		print $awards[$i].") added for episode S".$local->{'season'}."E".$local->{'episode'}."\n";
	}
	return;
}

sub writeXML {
	foreach my $hash_ref (@local_episodes) {
		print "Writing XML file to: ".$hash_ref->{directory}.$hash_ref->{filename}.".xml\n";
		my $path = $hash_ref->{directory}.$hash_ref->{filename}.".xml";
		open my $fh, '>:encoding(utf-8)', $path or die "open($path): $!";
  		XMLout($hash_ref, OutputFile => $fh,
  						  AttrIndent => 1,
  						  #GroupTags => { actors => 'actor' },
  						  NoAttr => 1,
  						  RootName => 'details',
  						  XMLDecl => '<?xml version="1.0" encoding="UTF-8"?>');
  		$fh->close;
	}
}

sub validateDateFormat {

	my ($date) = @_;
    my ($day, $month, $year) = undef;
    
    #$txt='14 January 1990';
    
    my $re1='(\\d+)';	# Integer Number 1
    my $re2='.*?';	# Non-greedy match on filler
    my $re3='((?:[a-z][a-z]+))';	# Word 1
    my $re4='.*?';	# Non-greedy match on filler
    my $re5='(\\d+)';	# Integer Number 2
    
    my $re=$re1.$re2.$re3.$re4.$re5;
    if ($date =~ m/$re/is) {
        $day=$1;
        $month=$2;
        $year=$3;
    }
    
    if ($month eq "January") {
        $month = "01";
    } elsif ($month eq "February") {
        $month = "02";
    } elsif ($month eq "March") {
        $month = "03";
    } elsif ($month eq "April") {
        $month = "04";
    } elsif ($month eq "May") {
        $month = "05";
    } elsif ($month eq "June") {
        $month = "06";
    } elsif ($month eq "July") {
        $month = "07";
    } elsif ($month eq "August") {
        $month = "08";
    } elsif ($month eq "September") {
        $month = "09";
    } elsif ($month eq "October") {
        $month = "10";
    } elsif ($month eq "November") {
        $month = "11";
    } elsif ($month eq "December") {
        $month = "12";
    } else {
        print "No valid month found: ".$month." . Setting value 00 instead.\n";
        $month = "00";
    }
    
    #return valid WDTV date stamp YYYY-MM-DD
    return $year."-".$month."-".$day;
    
}

sub validFiletype {
	my $ext = shift;
	
	my $re1='.*?';	# Non-greedy match on filler
	my $re2='((?:[a-z][a-z]+))';	# Word 1
	
	my $re=$re1.$re2;
	if ($ext =~ m/$re/is) {
		if ($1 eq "avi" || $1 eq "AVI" || $1 eq "mpg" || $1 eq "MPG" ||
			$1 eq "mpeg" || $1 eq "MPEG" || $1 eq "mkv" || $1 eq "MKV" ||
			$1 eq "wmv" || $1 eq "WMV" || $1 eq "mp4" || $1 eq "mp4" ||
			$1 eq "divx" || $1 eq "DIVX" || $1 eq "vob" || $1 eq "VOB" ||
			$1 eq "mov" || $1 eq "MOV" || $1 eq "ts" || $1 eq "TS") {
				
			#only checking for the most common video file types here
			#file type check should be disabled using -t for other valid types	
			print "Movie extension found: ".$1."\n"
				if $options{verbose};
			return 1;
				
		} else {
			print "Unknown extension found: ".$1."\n"
				if $options{verbose};
			return 0;
		}
	}
}

sub display_usage {
    print <<EOF;
Usage: $0 [OPTIONS]

Tag 'em!  WDTV Live Hub meta data tagger for TV shows

Creates XML files with meta data for TV Shows readable by WDTV Live Hub
  
Mandatory arguments to long options are mandatory for short options too.

  -d, --directory=DIR				Mandatory option. Specifies the
  						directory where episodes are located. 

  -t, --title="TITLE"				Mandatory option. TITLE of the TV
						show you want to tag. You have to use
						double quotes ("").
						
  -y, --year=YEAR				Release YEAR of the wanted show. Use
						this additional option in case using
						TITLE only returns the wrong result.
	
  -p, --prompt					Prompt before performing more than one check to
						read the season and episode. Mostly used only if
						wrong values were found.
  
  -s, --skip					Skips the file type check. This option is useful
						if you try to tag video files with unusual file
						extensions.

  -x, --xml					Writes a XML file for each valid episode.
  
  -v, --verbose					Prints a bunch of output and debug info.

  -h, --help					Display this help page and exit.
  
EOF

    exit 0;
}

sub usage_error {
    my $message = shift;
    if ($message) {
        $message =~ s/\s+$//;
        $message = "$0: $message\n";
    }
    else {
        $message = '';
    }
    die <<EOF;
${message}Usage: $0 [OPTIONS]
Try '$0 --help' for more information!
EOF
}
